Ce processus implique la définition d'une valeur initiale pour chaque propriété stockée sur cette instance et l'exécution de toute autre configuration ou initialisation requise avant que la nouvelle instance ne soit prête à être utilisée.

Les initialiseurs Swift ne retournent pas de valeur. Leur rôle principal est de s'assurer que les nouvelles instances d'un type sont correctement initialisées avant d'être utilisées pour la première fois.

Définition des valeurs initiales des propriétés stockées

Les propriétés stockées ne peuvent pas être laissées dans un état indéterminé.

Vous pouvez définir une valeur initiale pour une propriété stockée dans un initialiseur ou en attribuant une valeur de propriété par défaut dans le cadre de la définition de la propriété.

Un initialiseur est comme une méthode d'instance sans paramètre, écrite à l'aide du mot-clé init :


init() {
    // perform some initialization here
}

La structure ci-dessous définit un seul initialiseur, init, sans paramètre, qui initialise la température stockée avec une valeur de 32.0 :


struct Fahrenheit {
    var temperature: Double
    init() {
        temperature = 32.0
    }
}
var f = Fahrenheit()
print("The default temperature is \(f.temperature)° Fahrenheit")
// "The default temperature is 32.0° Fahrenheit"

Vous pouvez définir la valeur initiale d'une propriété stockée à partir d'un initialiseur, comme indiqué ci-dessus. Vous pouvez également spécifier une valeur de propriété par défaut dans le cadre de la déclaration de la propriété :


struct Fahrenheit {
    var temperature = 32.0
}

Personnalisation de l'initialisation

Vous pouvez personnaliser le processus d'initialisation avec des paramètres d'entrées et des types de propriétés facultatives, ou en attribuant des propriétés constantes lors de l'initialisation.

Les paramètres d'initialisation ont les mêmes capacités et la même syntaxe que les paramètres de fonction et de méthode.


struct Celsius {
    var temperatureInCelsius: Double
    init(fromFahrenheit fahrenheit: Double) {
        temperatureInCelsius = (fahrenheit - 32.0) / 1.8
    }
    init(fromKelvin kelvin: Double) {
        temperatureInCelsius = kelvin - 273.15
    }
}
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0

Comme pour les paramètres de fonction et de méthode, les paramètres d'initialisation peuvent avoir à la fois un nom de paramètre à utiliser dans le corps de l'initialiseur et une étiquette d'argument à utiliser lors de l'appel de l'initialiseur.

Cependant, les initialiseurs n'ont pas de nom de fonction d'identification avant leurs parenthèses comme le font les fonctions et les méthodes.

Si vous ne souhaitez pas utiliser une étiquette d'argument pour un paramètre d'initialisation, écrivez un trait de soulignement _ au lieu d'une étiquette d'argument explicite pour ce paramètre pour remplacer le comportement par défaut.


struct Celsius {
    var temperatureInCelsius: Double
    init(_ celsius: Double) {
        temperatureInCelsius = celsius
    }
}
let bodyTemperature = Celsius(37.0)
// bodyTemperature.temperatureInCelsius is 37.0

Types de propriétés facultatives ou optionnelles

Les propriétés de type optionel sont automatiquement initialisées avec une valeur de nil, indiquant que la propriété est délibérément destinée à n'avoir "pas encore de valeur" lors de l'initialisation.


class SurveyQuestion {
    var text: String
    var response: String?
    init(text: String) {
        self.text = text
    }
    func ask() {
        print(text)
    }
}
let cheeseQuestion = SurveyQuestion(text: "Do you like cheese?")
cheeseQuestion.ask()
// Prints "Do you like cheese?"
cheeseQuestion.response = "Yes, I do like cheese."

Attribution de propriétés constantes

Pour les instances de classes, une propriété constante ne peut être modifiée lors de l'initialisation que par la classe qui l'introduit. Elle ne peut pas être modifiée par une sous-classe.

Initialiseurs par membres

La structure Size reçoit automatiquement un initialiseur init(width: height:) par membre, que vous pouvez utiliser pour initialiser une nouvelle instance Size :


struct Size {
    var width = 0.0, height = 0.0
}
let twoByTwo = Size(width: 2.0, height: 2.0)

Lorsque vous appelez un initialiseur membre, vous pouvez omettre les valeurs de toutes les propriétés qui ont des valeurs par défaut :


let zeroByTwo = Size(height: 2.0)  // Prints "0.0 2.0"
let zeroByZero = Size()  // Prints "0.0 0.0"

Délégation d'initialiseurs pour les types de valeurs

Les initialiseurs peuvent appeler d'autres initialiseurs pour effectuer une partie de l'initialisation d'une instance. Ce processus, appelé délégation d'initialiseurs, évite la duplication de codes sur plusieurs initialiseurs.

Les types de valeurs (structures et énumérations) ne prennent pas en charge l'héritage, et leur processus de délégation d'initialiseurs est donc relativement simple, car ils ne peuvent déléguer qu'à un autre initialiseur qu'ils fournissent eux-mêmes.

Les classes, cependant, peuvent hériter d'autres classes. Cela signifie que les classes ont des responsabilités supplémentaires pour s'assurer que toutes les propriétés stockées dont elles héritent reçoivent une valeur appropriée lors de l'initialisation.

Pour les types de valeurs, vous utilisez self.init pour faire référence à d'autres initialiseurs du même type de valeurs lors de l'écriture de vos propres initialiseurs personnalisés. Vous ne pouvez appeler self.init qu'à partir d'un initialiseur.

Notez que si vous définissez un initialiseur personnalisé pour un type valeur, vous n'aurez plus accès à l'initialiseur par défaut (ou à l'initialiseur par membre, s'il s'agit d'une structure) pour ce type.


struct Size {
    var width = 0.0, height = 0.0
}

struct Point {
    var x = 0.0, y = 0.0
}

struct Rect {
    var origin = Point()
    var size = Size()
    init() {}
    init(origin: Point, size: Size) {
        self.origin = origin
        self.size = size
    }
    init(center: Point, size: Size) {
        let originX = center.x - (size.width / 2)
        let originY = center.y - (size.height / 2)
        self.init(origin: Point(x: originX, y: originY), size: size)
    }
}

Héritages et initialisations de classes

Toutes les propriétés stockées d'une classe, y compris toutes les propriétés que la classe hérite de sa superclasse, doivent se voir attribuer une valeur initiale lors de l'initialisation.

Swift définit deux types d'initialiseurs pour les types de classes afin de garantir que toutes les propriétés stockées reçoivent une valeur initiale. Ceux-ci sont connus comme des initialiseurs désignés et des initialiseurs pratiques.

Initialiseurs désignés et initialiseurs de commodité (ou pratiques)

Les initialiseurs désignés sont les principaux initialiseurs d'une classe. Un initialiseur désigné initialise complètement toutes les propriétés introduites par cette classe et appelle un initialiseur de superclasse approprié pour continuer le processus d'initialisation dans la chaîne de superclasse.

Les classes ont tendance à avoir très peu d'initialiseurs désignés, et il est assez courant qu'une classe n'en ait qu'un. Les initialiseurs désignés sont des points "d'entonnoir" à travers lesquels l'initialisation a lieu et à travers lesquels le processus d'initialisation continue le long de la chaîne de la superclasse.

Les initialiseurs de commodité sont secondaires et prennent en charge les initialiseurs d'une classe. Vous pouvez définir un initialiseur de commodité pour appeler un initialiseur désigné de la même classe que l'initialiseur de commodité avec certains des paramètres de l'initialiseur désigné définis sur les valeurs par défaut. Vous pouvez également définir un initialiseur pratique pour créer une instance de cette classe pour un cas d'utilisation ou un type de valeur d'entrée spécifique.

Vous n'êtes pas obligé de fournir des initialiseurs pratiques si votre classe n'en a pas besoin.

Les initialiseurs désignés pour les classes sont écrits de la même manière que les initialiseurs simples pour les types valeurs :

initialiseur swift
Initialiseur Swift - Source Apple

init(parameters) {
    statements
}

Les initialiseurs de commodité sont écrits dans le même style, mais avec le modificateur convenience placé avant le mot-clé init, séparé par un espace :


convenience init(parameters) {
    statements
}

Pour simplifier les relations entre les initialiseurs désignés et pratiques, Swift applique les trois règles suivantes pour les appels de délégation entre les initialiseurs :

Règle 1
Un initialiseur désigné doit appeler un initialiseur désigné à partir de sa superclasse immédiate.
Règle 2
Un initialiseur pratique doit appeler un autre initialiseur de la même classe.
Règle 3
Un initialiseur pratique doit finalement appeler un initialiseur désigné.

Un moyen simple de s'en souvenir est :

  • Les initialiseurs désignés doivent toujours déléguer vers le haut
  • Les initialiseurs de commodité doivent toujours déléguer à travers
L'initialisation de classe dans Swift est un processus en deux phases. Dans la première phase, chaque propriété stockée se voit attribuer une valeur initiale par la classe qui l'a introduite. Une fois que l'état initial de chaque propriété stockée a été déterminé, la deuxième phase commence et chaque classe a la possibilité de personnaliser davantage ses propriétés stockées avant que la nouvelle instance ne soit considérée comme prête à l'emploi.

Le compilateur de Swift effectue quatre vérifications de sécurité utiles pour s'assurer que l'initialisation en deux phases est terminée sans erreur :

  • Contrôle de sécurité 1
    Un initialiseur désigné doit s'assurer que toutes les propriétés introduites par sa classe sont initialisées avant de déléguer jusqu'à un initialiseur de superclasse.
  • Contrôle de sécurité 2
    Un initialiseur désigné doit déléguer jusqu'à un initialiseur de superclasse avant d'attribuer une valeur à une propriété héritée. Si ce n'est pas le cas, la nouvelle valeur attribuée par l'initialiseur désigné sera écrasée par la superclasse dans le cadre de sa propre initialisation.
  • Contrôle de sécurité 3
    Un initialiseur pratique doit déléguer à un autre initialiseur avant d'attribuer une valeur à une propriété (y compris les propriétés définies par la même classe). Si ce n'est pas le cas, la nouvelle valeur attribuée par l'initialiseur de commodité sera écrasée par l'initialiseur désigné de sa propre classe.
  • Contrôle de sécurité 4
    Un initialiseur ne peut pas appeler de méthodes d'instance, lire les valeurs de propriétés d'instance ou faire référence à une valeur self tant que la première phase d'initialisation n'est pas terminée.

Voici comment l'initialisation en deux phases se déroule, en fonction des quatre contrôles de sécurité ci-dessus :

La phase 1

  • Un initialiseur désigné ou pratique est appelé sur une classe.
  • La mémoire pour une nouvelle instance de cette classe est allouée. La mémoire n'est pas encore initialisée.
  • Un initialiseur désigné pour cette classe confirme que toutes les propriétés stockées introduites par cette classe ont une valeur. La mémoire de ces propriétés stockées est maintenant initialisée.
  • L'initialiseur désigné passe à un initialiseur de superclasse pour effectuer la même tâche pour ses propres propriétés stockées.
  • Cela continue la chaîne d'héritage de classe jusqu'à ce que le sommet de la chaîne soit atteint.
  • Une fois que le sommet de la chaîne est atteint et que la classe finale de la chaîne s'est assurée que toutes ses propriétés stockées ont une valeur, la mémoire de l'instance est considérée comme entièrement initialisée et la phase 1 est terminée.

Phase 2

  • En redescendant du haut de la chaîne, chaque initialiseur désigné dans la chaîne a la possibilité de personnaliser davantage l'instance. Les initialiseurs peuvent désormais accéder self et modifier ses propriétés, appeler ses méthodes d'instance, etc.
  • Enfin, tous les initialiseurs pratiques de la chaîne ont la possibilité de personnaliser l'instance et de l'utiliser self.

Héritage et remplacement de l'initialiseur

Les sous-classes Swift n'héritent pas par défaut de leurs initialiseurs de superclasse. Les initialiseurs de superclasse sont hérités dans certaines circonstances, mais uniquement lorsque cela est sûr et approprié.

Si vous souhaitez qu'une sous-classe personnalisée présente un ou plusieurs des mêmes initialiseurs que sa superclasse, vous pouvez fournir une implémentation personnalisée de ces initialiseurs dans la sous-classe.

Lorsque vous écrivez un initialiseur de sous-classe qui correspond à un initialiseur désigné par une superclasse , vous fournissez en fait un remplacement de cet initialiseur désigné. Par conséquent, vous devez écrire le modificateur override avant la définition d'initialisation de la sous-classe.

Comme pour une propriété, une méthode ou un indice substitué, la présence du modificateur override invite Swift à vérifier que la superclasse a un initialiseur désigné correspondant à remplacer, et valide que les paramètres de votre initialiseur de remplacement ont été spécifiés comme prévu.

Inversement, si vous écrivez un initialiseur de sous-classe qui correspond à un initialiseur de commodité de superclasse , cet initialiseur de commodité de superclasse ne peut jamais être appelé directement par votre sous-classe, selon les règles décrites ci-dessus. Donc inutile d'écrire le modificateur override.

L'exemple ci-dessous définit une classe de base appelée Vehicle. Cette classe de base déclare une propriété stockée appelée numberOfWheels. La propriété numberOfWheels est utilisée par une propriété calculée appelée description :


class Vehicle {
    var numberOfWheels = 0
    var description: String {
        return "\(numberOfWheels) wheel(s)"
    }
}

La classe Vehicle fournit une valeur par défaut pour sa seule propriété stockée et ne fournit elle-même aucun initialiseur personnalisé. En conséquence, il reçoit automatiquement un initialiseur par défaut.


class Bicycle: Vehicle {
    override init() {
        super.init()
        numberOfWheels = 2
    }
}

La sous-classe Bicycle définit un initialiseur, init(). Cet initialiseur désigné correspond à un initialiseur désigné de la superclasse de Bicycle, et ainsi la version de cet initialiseur est marquée avec le modificateur override.

Il commence par appeler super.init(), qui appelle l'initialiseur par défaut pour la superclasse Bicycle de la classe, Vehicle. Cela garantit que la propriété héritée numberOfWheels est initialisée avant d'avoir la possibilité de la modifier.


let bicycle = Bicycle()
print("Bicycle: \(bicycle.description)")  // Bicycle: 2 wheel(s)

Cet exemple définit une autre sous-classe de Vehicle, appelée Hoverboard. Dans son initialiseur, la classe Hoverboard définit uniquement sa propriété color. Au lieu de faire un appel explicite à super.init(), cet initialiseur s'appuie sur un appel implicite à l'initialiseur de sa superclasse pour terminer le processus.


class Hoverboard: Vehicle {
    var color: String
    init(color: String) {
        self.color = color
        // super.init() implicitly called here
    }
    override var description: String {
        return "\(super.description) in a beautiful \(color)"
    }
}

Les sous-classes peuvent modifier les propriétés des variables héritées lors de l'initialisation, mais ne peuvent pas modifier les propriétés des constantes héritées.

Héritage d'initialisation automatique

Les sous-classes n'héritent pas de leurs initialiseurs de superclasse par défaut. Cependant, les initialiseurs de superclasse sont automatiquement hérités si certaines conditions sont remplies. En pratique, cela signifie que vous n'avez pas besoin d'écrire des remplacements d'initialiseur dans de nombreux scénarios courants et que vous pouvez hériter de vos initialiseurs de superclasse avec un minimum d'effort chaque fois que cela est sûr.

  • Si votre sous-classe ne définit aucun initialiseur désigné, elle hérite automatiquement de tous ses initialiseurs désignés par superclasse.
  • Si votre sous-classe fournit une implémentation de tous ses initialiseurs désignés de superclasse - soit en les héritant selon la règle 1, soit en fournissant une implémentation personnalisée dans le cadre de sa définition - alors elle hérite automatiquement de tous les initialiseurs de commodité de superclasse.

L'exemple suivant montre les initialiseurs désignés, les initialiseurs pratiques et l'héritage d'initialisation automatique en action. Cet exemple définit une hiérarchie de trois classes appelées Food, RecipeIngredient et ShoppingListItem, et montre comment leurs initialiseurs interagissent :


class Food {
    var name: String
    init(name: String) {
        self.name = name
    }
    convenience init() {
        self.init(name: "[Unnamed]")
    }
}

Les classes n'ont pas d'initialiseur membre par défaut, et donc la classe food fournit un initialiseur désigné qui prend un seul argument appelé name. Cet initialiseur peut être utilisé pour créer une nouvelle instance food avec un nom spécifique :


let namedMeat = Food(name: "Bacon")
// namedMeat's name is "Bacon"

La classe food fournit également un initialiseur pratique init(), sans argument :


let mysteryMeat = Food()  // mysteryMeat's name is "[Unnamed]"

La deuxième classe de la hiérarchie est une sous-classe de Food appelée RecipeIngredient :


class RecipeIngredient: Food {
    var quantity: Int
    init(name: String, quantity: Int) {
        self.quantity = quantity
        super.init(name: name)
    }
    override convenience init(name: String) {
        self.init(name: name, quantity: 1)
    }
}

La figure ci-dessous montre la chaîne d'initialisation de la classe RecipeIngredient

initialiseur swift
Initialiseur Swift - Source Apple

Ces trois initialiseurs peuvent être utilisés pour créer de nouvelles instances RecipeIngredient :


let oneMysteryItem = RecipeIngredient()
let oneBacon = RecipeIngredient(name: "Bacon")
let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6)

La troisième et dernière classe de la hiérarchie est une sous-classe de RecipeIngredient appelée ShoppingListItem :


class ShoppingListItem: RecipeIngredient {
    var purchased = false
    var description: String {
        var output = "\(quantity) x \(name)"
        output += purchased ? " ✔" : " ✘"
        return output
    }
}

Comme il fournit une valeur par défaut pour toutes les propriétés qu'il introduit et ne définit aucun initialiseur lui-même, ShoppingListItem hérite automatiquement de tous les initialiseurs désignés et pratiques de sa superclasse :

Vous pouvez utiliser les trois initialiseurs hérités pour créer une nouvelle instance ShoppingListItem :


var breakfastList = [
    ShoppingListItem(),
    ShoppingListItem(name: "Bacon"),
    ShoppingListItem(name: "Eggs", quantity: 6),
]
breakfastList[0].name = "Orange juice"
breakfastList[0].purchased = true
for item in breakfastList {
    print(item.description)
}
// 1 x Orange juice ✔
// 1 x Bacon ✘
// 6 x Eggs ✘

Initialiseurs disponibles

Il est parfois utile de définir une classe, une structure ou une énumération pour laquelle l'initialisation peut échouer.

Pour faire face aux conditions d'initialisation qui peuvent échouer, définissez un ou plusieurs initialiseurs disponibles dans le cadre d'une définition de classe, structure ou énumération. Vous écrivez un initialiseur disponible en plaçant un point d'interrogation après le mot-clé init?.

Vous ne pouvez pas définir un initialiseur disponible et un initialiseur non disponible avec les mêmes types et noms de paramètres.

Par exemple, les initialiseurs disponibles sont implémentés pour les conversions de type numérique. Pour garantir que la conversion entre les types numériques conserve la valeur exacte, utilisez l'initialiseur init(exactly:). Si la conversion de type ne peut pas conserver la valeur, l'initialiseur échoue :


let wholeNumber: Double = 12345.0
let pi = 3.14159

if let valueMaintained = Int(exactly: wholeNumber) {
    print("\(wholeNumber) conversion to Int maintains value of \(valueMaintained)")
}
// Prints "12345.0 conversion to Int maintains value of 12345"

let valueChanged = Int(exactly: pi)
// valueChanged is of type Int?, not Int

if valueChanged == nil {
    print("\(pi) conversion to Int does not maintain value")
}
// Prints "3.14159 conversion to Int does not maintain value"

L'exemple ci-dessous définit une structure appelée Animal, avec une propriété constante appelée species. La structure Animal définit également un initialiseur disponible avec un seul paramètre appelé species. Cet initialiseur vérifie si la valeur species transmise à l'initialiseur est une chaîne vide. Si une chaîne vide est trouvée, un échec d'initialisation est déclenché. Sinon, la valeur de la propriété est définie et l'initialisation réussit :


struct Animal {
    let species: String
    init?(species: String) {
        if species.isEmpty { return nil }
        self.species = species
    }
}

Si vous transmettez une valeur de chaîne vide au paramètre species de l'initialiseur disponible, l'initialiseur déclenche un échec d'initialisation :


let anonymousCreature = Animal(species: "")
// anonymousCreature is of type Animal?, not Animal

if anonymousCreature == nil {
    print("The anonymous creature could not be initialized")
}
// Prints "The anonymous creature could not be initialized"

Initialiseurs disponibles pour les énumérations

Vous pouvez utiliser un initialiseur disponible pour sélectionner un cas d'énumération approprié basé sur un ou plusieurs paramètres. L'initialiseur peut alors échouer si les paramètres fournis ne correspondent pas à un cas d'énumération approprié.


enum TemperatureUnit {
    case kelvin, celsius, fahrenheit
    init?(symbol: Character) {
        switch symbol {
        case "K":
            self = .kelvin
        case "C":
            self = .celsius
        case "F":
            self = .fahrenheit
        default:
            return nil
        }
    }
}

let fahrenheitUnit = TemperatureUnit(symbol: "F")
if fahrenheitUnit != nil {
    print("Initialization succeeded.")
} // Initialization succeeded

let unknownUnit = TemperatureUnit(symbol: "X")
if unknownUnit == nil {
    print("Initialization failed.")
}  // Initialization failed

Initialiseurs disponibles pour les énumérations avec des valeurs brutes

Les énumérations avec des valeurs brutes reçoivent automatiquement un initialiseur disponible, init?(rawValue:) qui prend un paramètre appelé rawValued du type de valeur brute appropriée et sélectionne un cas d'énumération correspondant s'il en existe un, ou déclenche un échec d'initialisation si aucune valeur correspondante n'existe.


enum TemperatureUnit: Character {
    case kelvin = "K", celsius = "C", fahrenheit = "F"
}

let fahrenheitUnit = TemperatureUnit(rawValue: "F")
if fahrenheitUnit != nil {
    print("Initialization succeeded.")
}  // Initialization succeeded

Propagation de l'échec d'initialisation

Un initialiseur disponible d'une classe, d'une structure ou d'une énumération peut déléguer à un autre initialiseur disponible de la même classe, structure ou énumération. De même, un initialiseur disponible de sous-classe peut déléguer jusqu'à un initialiseur disponible de superclasse.

Dans les deux cas, si vous déléguez à un autre initialiseur qui provoque l'échec de l'initialisation, l'ensemble du processus d'initialisation échoue immédiatement et aucun autre code d'initialisation n'est exécuté.


class Product {
    let name: String
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}

class CartItem: Product {
    let quantity: Int
    init?(name: String, quantity: Int) {
        if quantity < 1 { return nil }
        self.quantity = quantity
        super.init(name: name)
    }
}

L'initialiseur disponible pour CartItem démarre en validant qu'il a reçu une valeur de quantity 1 ou plus. Si le quantity n'est pas valide, tout le processus d'initialisation échoue immédiatement et aucun autre code d'initialisation n'est exécuté. De même, l'initialiseur disponible pour Product vérifie la valeur name et le processus d'initialisation échoue immédiatement si la chaîne name est vide.

Remplacement d'un initialiseur disponible

Vous pouvez remplacer un initialiseur disponible de superclasse dans une sous-classe, comme tout autre initialiseur. Vous pouvez également remplacer un initialiseur disponible de superclasse par un initialiseur de sous-classe non disponible . Cela vous permet de définir une sous-classe pour laquelle l'initialisation ne peut pas échouer, même si l'initialisation de la superclasse est autorisée à échouer.

L'exemple ci-dessous définit une classe appelée Document. Cette classe modélise un document qui peut être initialisé avec une propriété name qui est une valeur de chaîne non vide ou nil, mais qui ne peut pas être une chaîne vide :


class Document {
    var name: String?
    // this initializer creates a document with a nil name value
    init() {}
    // this initializer creates document with a !empty name value
    init?(name: String) {
        if name.isEmpty { return nil }
        self.name = name
    }
}

Initialiser requis

Écrivez le modificateur required avant la définition d'un initialiseur de classe pour indiquer que chaque sous-classe de la classe doit implémenter cet initialiseur :


class SomeClass {
    required init() {
        // initializer implementation goes here
    }
}

Vous devez également écrire le modificateur required avant chaque implémentation de sous-classe d'un initialiseur requis, pour indiquer que l'exigence d'initialisation s'applique à d'autres sous-classes de la chaîne. Vous n'écrivez pas le modificateur override lors de la substitution d'un initialiseur désigné requis :


class SomeSubclass: SomeClass {
    required init() {
        // here, subclass implementation of required initializer       
    }
}